﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using iNet.IO;
using iNet.Tasks;

namespace iNet.Net
{
	/// <summary>
	/// 將網路文件下載至本機的工作。
	/// </summary>
	public class WebDocumentDownloadTask : Task
	{
		#region 欄位
		string _LocalFileName;
		WebRequest _Request;
		WebResponse _Response;
		WaitEvent _WaitEvent;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 WebDocumentDownloadTask 執行個體。
		/// </summary>
		public WebDocumentDownloadTask()
		{ }
		/// <summary>
		/// 初始化新的 WebDocumentDownloadTask 執行個體。
		/// </summary>
		/// <param name="request">網路文件的伺服器請求。</param>
		public WebDocumentDownloadTask(WebRequest request)
		{
			_Request = request;
		}
		#endregion
		#region Execute
		protected override bool Execute(TaskVersion version)
		{
			//取得初始化參數
			string localFileName;
			WebRequest request;
			if (!this.ReadValue<WebRequest>(version, ref _Request, out request) ||
				!this.ReadValue<string>(version, ref _LocalFileName, out localFileName))
				return true;

			//確認初始化參數
			if (request == null)
				return false;

			//顯示進度
			this.ReportProgress(version, null);
			
			//等待伺服器回應
			lock (this.SyncRoot)
			{
				_WaitEvent = new WaitEvent();
			}
			object[] asyncParams = new object[] { _WaitEvent, request, null, null };
			request.BeginGetResponse(this.GetResponseCallback, asyncParams);
			_WaitEvent.Wait();
			lock (this.SyncRoot)
			{
				_WaitEvent = null;
			}

			//中斷確認
			if (this.IsCanceling)
				return true;

			//取得伺服器回應
			WebResponse response = (asyncParams[2] as WebResponse);
			if (response == null)
				return false;

			//取得連線資料流
			Stream responseStream;
			try
			{
				responseStream = response.GetResponseStream();
			}
			catch
			{
				return false;
			}

			//取得資料大小
			long? dataSize = response.ContentLength;
			if (dataSize.Value < 0)
				dataSize = null;

			//建立暫存檔
			bool useTempFile = string.IsNullOrEmpty(localFileName);
			if (useTempFile)
			{
				try
				{
					localFileName = Path.GetTempFileName();
				}
				catch (IOException ex)
				{
					return false;
				}
			}

			//開啟本機檔案
			FileStream localFileStream;
			if (!FileUtility.TryOpenFile(localFileName, FileMode.Create, FileAccess.Write, 3000, out localFileStream))
				return false;

			//下載檔案
			bool errorOrCanceled = false;
			try
			{
				//建立資料流複製工作
				StreamCopyTask streamCopyTask = new StreamCopyTask(responseStream, localFileStream);
				streamCopyTask.CopySize = dataSize;

				//下載檔案
				switch (this.ExecuteChildTask(version, streamCopyTask, false))
				{
					case TaskState.Canceled:
						errorOrCanceled = true;
						return true;
					case TaskState.Error:
						errorOrCanceled = true;
						return false;
				}

				//儲存結果
				if (!this.WriteValue<WebResponse>(version, ref _Response, response) ||
					!this.WriteValue<string>(version, ref _LocalFileName, localFileName))
				{
					errorOrCanceled = true;
					return true;
				}
			}
			catch
			{
				errorOrCanceled = true;
				throw;
			}
			finally
			{
				//關閉本機檔案
				localFileStream.Close();

				//關閉網路連線
				responseStream.Close();

				//刪除暫存檔
				if (errorOrCanceled && useTempFile)
				{
					try
					{
						File.Delete(localFileName);
					}
					catch
					{ }
				}
			}

			//完成
			return true;
		}
		#endregion
		#region LocalFileName
		/// <summary>
		/// 取得或設定本機檔案路徑，設定為 Null (Visual Basic 為 Nothing) 或空字串表示下載至暫存檔。
		/// </summary>
		public string LocalFileName
		{
			get
			{
				return _LocalFileName;
			}
			set
			{
				//確認參數
				if (value != null && !FileUtility.IsValidPath(value))
					throw new ArgumentException();

				//設定參數
				this.SetInitializingParameter<string>(ref _LocalFileName, value);
			}
		}
		#endregion
		#region GetResponseCallback
		void GetResponseCallback(IAsyncResult ar)
		{
			//取得資料
			object[] asyncParams = ar.AsyncState as object[];

			//取得伺服器回應
			try
			{
				asyncParams[2] = (asyncParams[1] as WebRequest).EndGetResponse(ar);
			}
			catch (Exception ex)
			{
				asyncParams[3] = ex;
			}

			//通知已取得回應
			(asyncParams[0] as WaitEvent).Set();
		}
		#endregion
		#region OnCanceling
		protected override void OnCanceling(TaskEventArgs e)
		{
			//通知已取消
			lock (this.SyncRoot)
			{
				if (_WaitEvent != null)
					_WaitEvent.Set();
			}

			//呼叫基底
			base.OnCanceling(e);
		}
		#endregion
		#region OnChildTaskProgressChanged
		protected override void OnChildTaskProgressChanged(TaskVersion version, Task childTask, TaskProgressChangedEventArgs e)
		{
			//回報進度
			this.ReportProgress(version, e.Progress);

			//呼叫基底
			base.OnChildTaskProgressChanged(version, childTask, e);
		}
		#endregion
		#region Request
		/// <summary>
		/// 取得或設定網路文件的伺服器請求。
		/// </summary>
		public WebRequest Request
		{
			get
			{
				return _Request;
			}
			set
			{
				this.SetInitializingParameter<WebRequest>(ref _Request, value);
			}
		}
		#endregion
		#region ResetOverride
		protected override void ResetOverride()
		{
			//呼叫基底
			base.ResetOverride();

			//重設參數
			_Response = null;
		}
		#endregion
		#region Response
		/// <summary>
		/// 取得網路文件的伺服器回應。
		/// </summary>
		public WebResponse Response
		{
			get
			{
				return _Response;
			}
		}
		#endregion
	}
}
